#include <cserver/restapi.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/log/trivial.hpp>
#include <boost/format.hpp>
#include <pqxx/transaction.hxx>

#include <sstream>

namespace csrv{ namespace webcmd{
	static const char* K_this_command= "api/v1/common/moduser";

	static void sync_data(std::string& left, std::string& right){
		if (left.empty())
			left = right;
		else
			right = left;
	}

	void mod_user(Request& req, pqxx::connection& dbconn, SessionContext& sctx, const std::string& /*cmd*/ ){
		auto query = parseQuery(req.getParam("QUERY_STRING"));
		auto& callback = query["callback"];
		auto sessionid = req.getCookie("sessionid");

		UserInfo uinfo;
		std::string current_account;
		{
			std::lock_guard<std::mutex> guard(sctx.mutex);
			auto pos = sctx.activeSessions.find(sessionid);
			// assert( pos != master.sessions.activeSessions.end());
			uinfo = sctx.user_info.find(pos->second)->second;
			current_account = pos->second;
		}
		bool is_admin =  uinfo.role == "admin";


		StreamGroup sg(req, 4096);

		boost::property_tree::ptree ptree;
		read_json(sg.fin, ptree);
		UserInfo newInfo;
		auto account = ptree.get<std::string>("account", current_account);
		auto node_password = ptree.get_child_optional("password");
		auto node_current_password = ptree.get_child_optional("current_password");
		newInfo.displayName = ptree.get<std::string>("name", "");
		newInfo.role = ptree.get<std::string>("role", "");

		std::string current_password;
		if (node_password){
			newInfo.password = node_password.get().get_value<std::string>();
			newInfo.password = hash_password(account, newInfo.password);
		}
		if (node_current_password){
			current_password = node_current_password.get().get_value<std::string>();
			current_password = hash_password(current_account,current_password);
		}

		if (!is_admin && (
					(account != current_account)
					|| !newInfo.role.empty()
					|| (!current_password.empty() && current_password != uinfo.password)
					)
		   )
		{
			response_error(sg.fout, 403, "ErrPermission", "PermissionDenied", req);
			return;
		}

		// must provide the password to change self password 
		if (!newInfo.password.empty() && (current_password != uinfo.password) && (account == current_account)){
			response_error(sg.fout, 400, "ErrParameter", "BadPassword", req);
			return;
		}

		{
			std::lock_guard<std::mutex> guard(sctx.mutex);
			auto user_itr = sctx.user_info.find(account);
			if (user_itr == sctx.user_info.end()){
				response_error(sg.fout, 400, "ErrParameter", "AccountNotFound", req);
				return;
			}

			sync_data(newInfo.displayName, user_itr->second.displayName);
			sync_data(newInfo.password, user_itr->second.password);
			sync_data(newInfo.role, user_itr->second.role);
			newInfo.userid = user_itr->second.userid;
		}
		pqxx::work w(dbconn);
		w.prepared("moduser")(newInfo.displayName)(newInfo.password)(newInfo.role)(newInfo.userid).exec();
		w.commit();

		response_header(sg.fout, 200);
		if (!callback.empty()) sg.fout << callback << "(";
		sg.fout << boost::format(R"({
"func":"moduser",
"args":{"account":%1%},
"ret":{"success":true, "message":""}
})") 
			% escape_string(account) ;
		if (!callback.empty()) sg.fout << ")";

	}

}
static CommandAddHelper k_registerHandler("POST", webcmd::K_this_command, &webcmd::mod_user, {"admin", "user"});
}


